/*
 * DemoUtil.java
 *
 * Research In Motion Limited proprietary and confidential
 * Copyright Research In Motion Limited, 2011-2011
 */
package com.allkapps.bbmopenchat.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

import com.allkapps.bbmopenchat.BBMBridge;

import net.rim.blackberry.api.bbm.platform.io.BBMPlatformChannel;
import net.rim.blackberry.api.bbm.platform.io.BBMPlatformConnection;
import net.rim.blackberry.api.bbm.platform.io.IOErrorCode;
import net.rim.blackberry.api.browser.URLEncodedPostData;
import net.rim.device.api.io.IOUtilities;
import net.rim.device.api.io.URI;
import net.rim.device.api.math.Fixed32;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.PopupScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.ui.picker.FilePicker;
import net.rim.device.api.util.IntEnumeration;
import net.rim.device.api.util.IntMultiMap;

/**
 * Some utility methods for the demo.
 * 
 */
public class BBMUtils {

    // The persistent object key for the saved PINS/PPIDs
    private static final long SAVED_PINS_KEY = 0xa0fc43c0a25a8eeL; // com.rim.samples.device.bbm.sdkdemo.util.DemoUtil.SAVED_PINS_KEY

    private static PersistentObject _savedPINMapPersistentObject;
    private static IntMultiMap _savedPINMap = new IntMultiMap();

    // Check to see if profile box icons are registered
    private static boolean _isProfileBoxIconsRegistered;
    // Icons to be registered with the profile box
    public static final String[] ICONS = { "apple.png", "pear.png", "orange.png" };
    static {
        // load the PINs and PPIDs from persistent store.
        _savedPINMapPersistentObject = PersistentStore.getPersistentObject( SAVED_PINS_KEY );

        _savedPINMap = (IntMultiMap) _savedPINMapPersistentObject.getContents();

        if( _savedPINMap == null ) {
            _savedPINMap = new IntMultiMap();
            _savedPINMapPersistentObject.setContents( _savedPINMap );
            _savedPINMapPersistentObject.commit();
        }

    }

    /**
     * This method looks for JPEG files only.
     * 
     * @return the file path of the picture picked
     */
    public static String showPicturePicker() {
        FilePicker filePicker = FilePicker.getInstance();
        // Just show the jpg files for selection for now
        filePicker.setFilter( ".jpg" );
        String filePath = filePicker.show();
        return filePath;
    }

    /**
     * Gets the encoded image from a filePath of the selected image
     * 
     */
    public static EncodedImage getEncodedImage( String filePath ) throws IOException {

        EncodedImage image = null;
        FileConnection fileConn = null;
        InputStream fileInputStream = null;

        if( filePath == null ) {
            return image;
        }

        try {
            fileConn = (FileConnection) Connector.open( filePath, Connector.READ );
            fileInputStream = fileConn.openInputStream();
            // If no exception is thrown, then the URI is valid, but the file
            // may or may not exist.
            if( fileConn != null && fileConn.exists() ) {
                if( fileInputStream != null ) {
                    byte[] data = IOUtilities.streamToBytes( fileInputStream );
                    image = EncodedImage.createEncodedImage( data, 0, data.length );
                }
            }
        } finally {
            closeConnection( fileConn );
            closeInputStream( fileInputStream );
            fileConn = null;
            fileInputStream = null;
        }

        return image;
    }

    /**
     * Prompts the user to pick a picture. This is the same as calling <code>pickPicture(pickerListener, -1, -1)</code>.
     * 
     * @param pickerListener
     *            the listener to call on success and failure events.
     * 
     * @throws NullPointerException
     *             if <code>pickerListener</code> is null.
     */
    public static void pickPicture( final PicturePickerListener pickerListener ) {
        pickPicture( pickerListener, -1, -1 );

    }

    /**
     * 
     * Prompts the user to pick a picture and resizes appropriately.
     * <p>
     * The listeners are called on success and failure events.
     * <p>
     * 
     * This method looks for JPEG files only.
     * 
     * @throws IOException
     *             if there was an error accessing the file or file system.
     * 
     * @param pickerListener
     *            the listener that's called for successes and failures.
     * @param maxWidth
     *            the maximum width that the image must fit in. It may be scaled down, but not scaled up.
     * @param maxHeight
     *            the maximum height that the image must fit in. It may be scaled down, but not scaled up.
     * 
     * @throws NullPointerException
     *             if <code>pickerListener</code> is null.
     */
    public static void pickPicture( final PicturePickerListener pickerListener, final int maxWidth, final int maxHeight ) {
        if( pickerListener == null ) {
            throw new NullPointerException( "Picker listener can't be null." );
        }

        final PopupScreen screen = new PopupScreen( new VerticalFieldManager() );
        final LabelField status = new LabelField();
        screen.add( status );

        final String filepath = BBMUtils.showPicturePicker();

        if( filepath == null ) {
            return;
        }

        final String filename = URI.getFile( filepath );

        Worker.getInstance().addToQueue( new Runnable() {
            public void run() {
                try {
                    EncodedImage newDisplayPicture = BBMUtils.getEncodedImage( filepath );

                    if( maxWidth > 0 && maxHeight > 0 ) {
                        synchronized( UiApplication.getEventLock() ) {
                        }
                        newDisplayPicture = sizeImage( newDisplayPicture, maxWidth, maxHeight );
                    }

                    if( newDisplayPicture != null ) {
                        if( screen.isDisplayed() ) {
                            synchronized( UiApplication.getEventLock() ) {
                                UiApplication.getUiApplication().popScreen( screen );
                            }
                        }

                        // Need to call listener on event thread because
                        // setDisplayPicture needs it.
                        final EncodedImage displayPicture = newDisplayPicture;
                        UiApplication.getUiApplication().invokeLater( new Runnable() {
                            public void run() {
                                pickerListener.picturePicked( displayPicture, -1 );
                            }
                        } );
                    }
                } catch( IllegalArgumentException e ) {
                    pickerListener.pickingPictureFailed( e, filepath );
                } catch( IOException e ) {
                    pickerListener.pickingPictureFailed( e, filepath );
                } catch( OutOfMemoryError e ) {
                    pickerListener.pickingPictureFailed( e, filepath );
                } finally {
                    if( screen.isDisplayed() ) {
                        synchronized( UiApplication.getEventLock() ) {
                            UiApplication.getUiApplication().popScreen( screen );
                        }
                    }
                }
            }
        } );
    }

    /**
     * 
     * Scales the image to fit within the specified width and height. If the image is smaller than the given dimensions, then
     * nothing happens.
     * 
     * @param image
     *            the image to resize
     * @param width
     *            the maximum width, if less than 1, then no change is made.
     * @param height
     *            the maximum height, if less than 1, then no change is made.
     * 
     * @return the resized image or the same image if width or height is less than 1.
     */
    public static EncodedImage sizeImage( EncodedImage image, int width, int height ) {
        if( width <= 0 || height <= 0 ) {
            return image;
        }

        // Don't distort the image. Just fit it within the parameters.
        EncodedImage result = null;
        if( image != null ) {
            int imageWidth = image.getWidth();
            int imageHeight = image.getHeight();

            if( imageWidth <= width && imageHeight <= height ) {
                return image;
            }

            int currentWidthFixed32 = Fixed32.toFP( imageWidth );
            int currentHeightFixed32 = Fixed32.toFP( imageHeight );
            int requiredWidthFixed32 = Fixed32.toFP( width );
            int requiredHeightFixed32 = Fixed32.toFP( height );

            // which edge requires the most scaling?
            // That will determine the scaling factor
            double scaleByWidth = (double) imageWidth / width;
            double scaleByHeight = (double) imageHeight / height;

            int scaleFixed32 = scaleByWidth > scaleByHeight ? Fixed32.div( currentWidthFixed32, requiredWidthFixed32 ) : Fixed32
                    .div( currentHeightFixed32, requiredHeightFixed32 );

            result = image.scaleImage32( scaleFixed32, scaleFixed32 );
        }
        return result;
    }

    /**
     * Displays a status message when picking a picture fails.
     * 
     * @param t
     *            the throwable
     * @param filepath
     *            a path to the exception.
     */
    public static void showPickPictureFailureMessage( Throwable t, String filepath ) {
        String message = "Hi";
        if( t instanceof OutOfMemoryError ) {
        } else if( t instanceof IllegalArgumentException ) {
        } else if( t instanceof IOException ) {
        }

        final String status = message;
        if( status != null ) {
            UiApplication.getUiApplication().invokeLater( new Runnable() {
                public void run() {
                    Dialog.inform( status );
                }
            } );
        }
    }

    /**
     * Closes an input stream without throwing IOExceptions.
     * 
     * @param is
     */
    public static void closeInputStream( InputStream is ) {
        try {
            if( is != null ) {
                is.close();
            }
        } catch( IOException e ) {
            // don't throw exceptions while cleaning up
        }
    }

    /**
     * Closes a connection without throwing any IOExceptions
     * 
     * @param connection
     */
    public static void closeConnection( Connection connection ) {
        try {
            if( connection != null ) {
                connection.close();
            }
        } catch( IOException e ) {
            // don't throw exceptions while cleaning up
        }
    }

    /**
     * Gets a list of saved pins.
     * 
     * @return a list of saved pins.
     */
    public static String[] getSavedPINs() {
        IntEnumeration pinEnum = _savedPINMap.keys();
        String[] pinStrings = new String[ _savedPINMap.size() ];

        int index = 0;
        while( pinEnum.hasMoreElements() ) {
            pinStrings[ index++ ] = Integer.toHexString( pinEnum.nextElement() );
        }

        return pinStrings;
    }

    public static String[] getSavedPPIDs() {
        Enumeration ppidEnum = _savedPINMap.elements();
        Vector ppidVector = new Vector();
        Hashtable knownPPIDs = new Hashtable();

        while( ppidEnum.hasMoreElements() ) {
            String ppid = (String) ppidEnum.nextElement();

            if( !knownPPIDs.containsKey( ppid ) ) {
                ppidVector.addElement( ppid );
                knownPPIDs.put( ppid, ppid );
            }

        }

        String[] ppids = new String[ ppidVector.size() ];
        ppidVector.copyInto( ppids );

        return ppids;
    }

    /**
     * Clears out the saved PINs and PPIDs.
     * 
     * @param ppid PPID to remove.
     */
    public static void removeAllPublicConnectionInfo() {
        _savedPINMap.clear();
        _savedPINMapPersistentObject.commit();
    }
    
    /**
     * Only good PINs and PPIDs should be stored. This method does not check if it's good or not. "Good" means no illegal argument
     * exception was thrown when sending a join request.
     * 
     * @param pin
     * @param ppid
     */
    public static void savePublicConnectionInfo( int pin, String ppid ) {
        _savedPINMap.add( pin, ppid );
        _savedPINMapPersistentObject.commit();
    }

    /**
     * Only good PINs and PPIDs should be stored. This method does not check if it's good or not. "Good" means no illegal argument
     * exception was thrown when sending a join request.
     * 
     * @param pin
     * @param ppid
     */
    public static void savePublicConnectionInfo( String pinString, String ppid ) {
        savePublicConnectionInfo( parsePin( pinString ), ppid );
    }

    /**
     * Parses a PIN string into a PIN.
     * 
     * @param pinString
     *            a hex PIN
     * @return an integer representation of the PIN string
     * 
     * @throws NumberFormatException
     *             if pin is not hex formatted.
     */
    public static int parsePin( String pinString ) {
        long longPin = Long.parseLong( pinString, 16 );
        return (int) ( longPin & 0x0FFFFFFFFL );
    }

    public static String getConnectionName( BBMPlatformConnection connection ) {
        final boolean isChannel = connection instanceof BBMPlatformChannel;
        final boolean isPublic = connection.isPublic();

        return "";
    }

    public static String translateIOErrorCode( int code ) {
        // TODO Use resource instead
        switch( code ) {
            case IOErrorCode.FILE_TRANSFER_FILE_NOT_FOUND:
            	return "File not found";
            case IOErrorCode.FILE_TRANSFER_USER_CANCELED:
            	return "User canceled file transfer";
            case IOErrorCode.FILE_TRANSFER_FILE_SIZE_EXCEEDED:
            	return "File exceeded size";
            case IOErrorCode.FILE_TRANSFER_BAD_FILE_TYPE:
            case IOErrorCode.FILE_TRANSFER_BAD_CONTACT:
            	return "Bad Contact";
            case IOErrorCode.FILE_TRANSFER_FILE_FORWARD_LOCKED:
            default:
                return "IDK";
        }
    }

    public static void registerProfileBoxIcons() {
        if( !_isProfileBoxIconsRegistered ) {
            int length = ICONS.length;
            for( int iconId = 0; iconId < length; iconId++ ) {
                String iconName = ICONS[ iconId ];
                EncodedImage icon = EncodedImage.getEncodedImageResource( iconName );
                BBMBridge.getInstance().registerProfileBoxIcon( iconId, icon );
            }
            _isProfileBoxIconsRegistered = true;
        }
    }
    
    public static boolean isStringValid(String text) {
        return text != null && text.length() != 0;
    }
    
    public static String getEncodedPPID(String ppId){
		URLEncodedPostData d = new URLEncodedPostData(URLEncodedPostData.DEFAULT_CHARSET, false);
		d.append("ppId", ppId);
		String encoded = d.toString();
		return encoded.substring(encoded.toString().indexOf("=")+1);
    }

}
